﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace RoadSystemTest
{
    public class Lane : ILaneObject
    {
        private const double COLLISION_SIZE_CAR = 100;
        private const double COLLISION_SIZE_END = 1;
        public Sites Site { get; private set; }
        public Sites GetSite() { return Site; }
        public LaneTypes LaneType { get; private set; }
        public LaneTypes GetLaneType() { return LaneType; }
        public Lane NextLane { get; private set; }

        //Az autókkal csak az interfészen át kommunikál
        public List<ICarObject> Cars { get; private set; }
        public List<bool> CarsActivity { get; private set; }

        public ITrafficObject Parent { get; private set; }
        public ITrafficObject GetParent()
        { 
            return (ITrafficObject)this; 
        }
        public bool CanEnter { get; private set; }
        private Path _path;
        private Point _startPosition;
        public Point StartPosition
        {
            get
            {
                return _startPosition;
            }
            set
            {
                _startPosition = new Point(value);
                _startPosition.Translate(Parent.GetPosition());
            }
        }
        private Point _endPosition;
        public Point EndPosition
        {
            get
            {
                return _endPosition;
            }
            set
            {
                _endPosition = new Point(value);
                _endPosition.Translate(Parent.GetPosition());
            }
        }
        public Point GetStartPosition()
        {
            return StartPosition;
        }
        public Point GetEndPosition()
        {
            return EndPosition;
        }
       
        //A lámpa váltása hívja, beállítja, hogy beléphet-e, és ha igen, kiadja az eseményt az éppen várakozó autóknak
        public void SetCanEnter(bool a)
        {
            CanEnter = a;
        }

        public Lane(ITrafficObject parent, Sites s, LaneTypes type)
        {
            Parent = parent;
            Site = s;
            LaneType = type;
            Cars = new List<ICarObject>();
            CarsActivity = new List<bool>();
            CanEnter = true;
            _path = new Path();            
        }

        public void Connect(Lane l)
        {
            NextLane = l;
        }

        public void Disconnect()
        {
            NextLane = null;
        }

        private void CalculateNewPosition(ICarObject car)
        {
            Point newPos = car.GetPosition();
            int carSpeed = car.GetSpeed();
            if (Site == Sites.Horizontal && LaneType == LaneTypes.Forward)
            {
                newPos.X = newPos.X + carSpeed;
            }
            if (Site == Sites.Horizontal && LaneType == LaneTypes.Backward)
            {
                newPos.X = newPos.X - carSpeed;
            }
            if (Site == Sites.Vertical && LaneType == LaneTypes.Forward)
            {
                newPos.Y = newPos.Y - carSpeed;
            }
            if (Site == Sites.Vertical && LaneType == LaneTypes.Backward)
            {
                newPos.Y = newPos.Y + carSpeed;
            }
            car.Move(newPos);
        }

        //Ellenőrzi, hogy mozoghat-e az autó, és ettől függően válaszol neki
        private void Car_MoveLane(object sender, TrafficEventArgs e)
        {
            ICarObject car = e.car;
            Point position = car.GetPosition();

            if (IsCollisionWithCarAtEndPosition(car, car.GetSpeed()))
            {
                //Az autó a sáv végén van
                EndCar(car);
            }
            else
            {
                //Az autó nincs a sáv végén
                if (!IsCollisionWithOtherCars(car))
                {
                    MoveCar(car);
                }
            }
        }

        private void Car_GetParent(object sender, TrafficEventArgs e)
        {
            // sender, e.car : a Car aminek a parent-jét kérdezzük le (in)
            // e.lane : a Lane ami a parentje (out)
            e.lane = this;
        }

        private void SetEvents(ICarObject a)
        {
            a.ResetEvents();
            a.OnGetParent += new TrafficEventHandler(Car_GetParent);
            a.OnMoveLane += new TrafficEventHandler(Car_MoveLane);
        }

        public bool AddCar(ICarObject car)
        {
            if (CanEnter)
            {
                if (IsCollisionAtStartPosition())
                {
                    return false;
                }
                SetEvents(car);                
                Cars.Add(car);
                CarsActivity.Add(true);
                car.Move(this.StartPosition, (this.Site == Sites.Horizontal)? 0: 90);
                return true;
            }
            return false;
        }

        private void EndCar(ICarObject car)
        {            
            if (this.NextLane != null)
            {
                if (this.NextLane.AddCar(car))
                {
                    RemoveCarsLeftFromBefore(car);
                }
            }
            else
            {
                int index = Cars.IndexOf(car);
                if (CarsActivity[index])
                {                    
                    CarsActivity[index] = false;
                }
                if (car.IsDisappeared())
                {
                    RemoveCarsLeftFromBefore(car);
                    ExitLane(car);
                }
                else
                {
                    car.Disappear();
                }                   
            }
        }

        private bool IsCollisionAtStartPosition(double limit = COLLISION_SIZE_CAR)
        {            
            return IsCollisionAtPosition(this.StartPosition, limit);
        }

        private bool IsCollisionAtPosition(Point pos, double limit = COLLISION_SIZE_CAR)
        {
            for (int i = 0; i < Cars.Count; i++)
            {
                ICarObject iCar = Cars[i];
                bool isCarActive = CarsActivity[i];
                //if (!iCar.IsNowhereToGo())
                if(isCarActive)
                {
                    if (pos.Distance(iCar.GetPosition()) < limit)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        private bool IsCollisionWithOtherCars(ICarObject car, double limit = COLLISION_SIZE_CAR)
        {
            int index = Cars.IndexOf(car);
            if(index > -1)
            {
                return IsCollisionWithOtherCars(index, limit);
            }
            return false;
        }

        private bool IsCollisionWithOtherCars(int index, double limit = COLLISION_SIZE_CAR)
        {
            ICarObject car = Cars[index];
            for (int i = index - 1; i >= 0; i--)
            {
                ICarObject iCar = Cars[i];
                bool isCarActive = CarsActivity[i];
                if (iCar != car && isCarActive)//!iCar.IsNowhereToGo())
                {
                    if (car.GetPosition().Distance(iCar.GetPosition()) < limit)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        private bool IsCollisionWithCarAtEndPosition(ICarObject car, double limit = COLLISION_SIZE_END)
        {
            return IsCollisionWithCarAtPosition(car, this.EndPosition, limit);
        }

        private bool IsCollisionWithCarAtPosition(ICarObject car, Point pos, double limit = COLLISION_SIZE_END)
        {
            return car.GetPosition().Distance(pos) < limit;
        }

        private void MoveCar(ICarObject car)
        {
            Point carPos = car.GetPosition();
            int carSpeed = car.GetSpeed();
            double angle;

            if (Site == Sites.Vertical)
            {
                angle = 90;
            }
            else
            {
                angle = 0;
            }
            if (Site == Sites.Horizontal && LaneType == LaneTypes.Forward)
            {
                carPos.X = carPos.X + carSpeed;
            }
            if (Site == Sites.Horizontal && LaneType == LaneTypes.Backward)
            {
                carPos.X = carPos.X - carSpeed;
            }
            if (Site == Sites.Vertical && LaneType == LaneTypes.Forward)
            {
                carPos.Y = carPos.Y - carSpeed;
            }
            if (Site == Sites.Vertical && LaneType == LaneTypes.Backward)
            {
                carPos.Y = carPos.Y + carSpeed;
            }
            car.Move(carPos, angle);
        }

        private void RemoveCarsLeftFromBefore(ICarObject car)
        {
            int index = Cars.IndexOf(car);
            if (index >= 0)
            {
                Cars.RemoveRange(0, index);
                CarsActivity.RemoveRange(0, index);
            }
        }

        public void RemoveAllCars()
        {
            foreach (Car c in Cars)
            {
                c.ResetEvents();
            }
            Cars.Clear();
            CarsActivity.Clear();
        }

        public event TrafficEventHandler OnExitLane;
        public void ExitLane(ICarObject car)
        {
            if (OnExitLane != null)
                OnExitLane(this, new TrafficEventArgs { car = car });
        }
    }
}
